﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.Collections.Generic;
using System.Reflection;

namespace Pittypat.Web
{
    /// <summary>
    /// 一个请求中的功能信息。
    /// </summary>
    sealed class FunctionInfo
    {
        // 功能隶属模块的信息
        private ModuleInfo module;
        // 功能元数据信息
        private FunctionMeta meta;
        // 功能的 FunctionArgs 和 FunctionModel 类型
        private FunctionTypes types;
        // 功能对应的方法
        private MethodInfo method;
        // 当前 FunctionModel 对象
        private object funcModel;
        // 功能的路径
        private string path;

        /// <summary>
        /// 使用模块信息、类型、元数据信息、目标方法、功能模型对象以及路径初始化 FunctionInfo 类的新实例。
        /// </summary>
        /// <param name="path">功能的完整路径。</param>
        /// <param name="moduleInfo">模块信息。</param>
        /// <param name="types">类型信息。</param>
        /// <param name="meta">元数据信息。</param>
        /// <param name="method">目标方法。</param>
        /// <param name="funcModel">功能模型对象。</param>
        internal FunctionInfo(string path, ModuleInfo moduleInfo, FunctionTypes types, FunctionMeta meta, MethodInfo method, object funcModel)
        {
            this.module = moduleInfo;
            this.meta = meta;
            this.types = types;
            this.method = method;
            this.funcModel = funcModel;
            this.path = path;
        }

        /// <summary>
        /// 获取开发者代码。
        /// </summary>
        internal string DeveloperCode
        {
            get
            {
                return this.module.AppConfig.Developer;
            }
        }

        /// <summary>
        /// 获取 App 标识符。
        /// </summary>
        internal string AppIdentifier
        {
            get
            {
                return this.module.AppConfig.Identifier;
            }
        }

        /// <summary>
        /// 获取模块标识符。
        /// </summary>
        internal string ModuleIdentifier
        {
            get
            {
                return this.module.Config.Identifier;
            }
        }

        /// <summary>
        /// 获取功能标识符。
        /// </summary>
        internal string Identifier
        {
            get
            {
                return this.meta.Config.Identifier;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示目标模块是否允许匿名用户访问。
        /// </summary>
        internal bool AllowAnonymous
        {
            get
            {
                return this.module.Attribute.AllowAnonymous;
            }
        }

        /// <summary>
        /// 获取目标功能对应的方法。
        /// </summary>
        internal MethodInfo Method
        {
            get
            {
                return this.method;
            }
        }

        /// <summary>
        /// 获取目标模块信息。
        /// </summary>
        internal ModuleInfo ModuleInfo
        {
            get
            {
                return this.module;
            }
        }

        /// <summary>
        /// 获取目标模块的对象实例。
        /// </summary>
        internal Module Module
        {
            get
            {
                return this.module.Module;
            }
        }

        /// <summary>
        /// 获取功能的元数据信息。
        /// </summary>
        internal FunctionMeta Meta
        {
            get
            {
                return this.meta;
            }
        }

        /// <summary>
        /// 获取功能的 FunctionModel。
        /// </summary>
        internal object FunctionModel
        {
            get
            {
                return this.funcModel;
            }
        }

        /// <summary>
        /// 获取功能的完整路径，格式为：开发者代码.应用标识符.模块标识符.功能标识符。
        /// </summary>
        internal string Path
        {
            get
            {
                return this.path;
            }
        }

        #region FunctionArgs 和 FunctionModel 类型的缓存

        #region FunctionTypes

        /// <summary>
        /// 缓存中的功能相关的类型。
        /// </summary>
        internal sealed class FunctionTypes
        {
            private Type argsType;
            private Type funcModelType;

            /// <summary>
            /// 使用目标 FunctionArgs 和 FunctionModel 类型初始化 FunctionTypes 的新实例。
            /// </summary>
            /// <param name="argsType">FunctionArgs 类型。</param>
            /// <param name="funcModelType">FunctionModel 类型。</param>
            internal FunctionTypes(Type argsType, Type funcModelType)
            {
                this.argsType = argsType;
                this.funcModelType = funcModelType;
            }

            /// <summary>
            /// 获取 FunctionArgs 类型。
            /// </summary>
            internal Type ArgsType
            {
                get
                {
                    return this.argsType;
                }
            }

            /// <summary>
            /// 获取 FunctionModel 类型。
            /// </summary>
            internal Type FuncModelType
            {
                get
                {
                    return this.funcModelType;
                }
            }
        }

        #endregion

        // 缓存字典，字典的键为功能的完整路径
        static readonly Dictionary<string, FunctionTypes> cachedTypes = new Dictionary<string, FunctionTypes>(512, StringComparer.OrdinalIgnoreCase);
        // 对字典进行同步访问的对象
        static readonly object cachedTypesLock = new object();

        /// <summary>
        /// 获取指定功能路径的类型信息。
        /// </summary>
        /// <param name="developerCode">开发者代码。</param>
        /// <param name="appIdentifier">App 标识符。</param>
        /// <param name="moduleIdentifier">模块标识符。</param>
        /// <param name="functionIdentifier">功能标识符。</param>
        /// <param name="path">功能的路径。</param>
        /// <returns>功能的类型信息。</returns>
        static FunctionTypes GetTypes(string developerCode, string appIdentifier, string moduleIdentifier, string functionIdentifier, string path)
        {
            FunctionTypes types = null;

            lock (cachedTypesLock)
            {
                if (!cachedTypes.TryGetValue(path, out types))
                {
                    // 加载功能的参数对象模型的类型
                    var argsType = FindArgsType(developerCode, appIdentifier, moduleIdentifier, functionIdentifier);

                    if (argsType == null)
                    {
                        return null;
                    }
                    
                    var funcModelType = typeof(FunctionModel<>).MakeGenericType(argsType);
                    if (funcModelType == null)
                    {
                        return null;
                    }

                    types = new FunctionTypes(argsType, funcModelType);

                    // 添加到缓存中
                    cachedTypes.Add(path, types);
                }
            }

            return types;
        }

        /// <summary>
        /// 查找指定路径的功能的 FunctionArgs 类型。
        /// </summary>
        /// <param name="developerCode">开发者代码。</param>
        /// <param name="appIdentifier">App 标识符。</param>
        /// <param name="moduleIdentifier">模块标识符。</param>
        /// <param name="functionIdentifier">功能标识符。</param>
        /// <returns>功能的 FunctionArgs 类型，如果没有找到，则返回 null。</returns>
        static Type FindArgsType(string developerCode, string appIdentifier, string moduleIdentifier, string functionIdentifier)
        {
            var assembly = AssemblyHelper.FindAssembly(string.Format("{0}.{1}.Api", developerCode, appIdentifier));
            if (assembly != null)
            {
                var type = assembly.GetType(string.Format("{0}.{1}.{2}.Args.{3}", developerCode, appIdentifier, moduleIdentifier, functionIdentifier), false, true);

                if (type != null)
                {
                    if (type.IsClass && !type.IsAbstract && !type.IsGenericType && type.IsSubclassOf(typeof(FunctionArgs)))
                    {
                        return type;
                    }
                }
            }

            return null;
        }

        #endregion

        /// <summary>
        /// 根据请求路径查找目标功能并创建一个代表功能信息的 FunctionInfo 对象。
        /// </summary>
        /// <param name="requestPath">请求路径。</param>
        /// <param name="createModel">指示是否应该创建 FunctionModel 对象。在上传文件时，是不需要创建 FunctionModel 对象的。</param>
        /// <returns>一个 FunctionInfo 对象，如果没有找到目标功能，则返回 null。</returns>
        internal static FunctionInfo Create(string requestPath, bool createModel)
        {
            if (string.IsNullOrEmpty(requestPath))
            {
                // 无效的路径
                return null;
            }

            var names = requestPath.Substring(1).Split('/');

            if (names.Length < 4)
            {
                return null;
            }

            // 检查四个部分是否是有效的标识符：开发者代码、应用标识符、模块标识符和功能标识符
            for (int i = 0; i < 4; ++i)
            {
                if (!names[i].IsIdentifier())
                {
                    return null;
                }
            }

            // 首先查找模块信息
            var moduleInfo = ModuleInfo.Get(names[0], names[1], names[2]);
            if (moduleInfo == null)
            {
                return null;
            }
            
            if (moduleInfo.Attribute.Presentation == ModulePresentation.Internal)
            {
                // 不能对外公开访问
                return null;
            }

            if (moduleInfo.IsMaintenance)
            {
                // 返回一个指示模块正在维护的 FunctionInfo
                return moduleInfo.MaintenanceFunctionInfo;
            }

            // 查找 Api 的功能方法
            var method = moduleInfo.Type.GetMethod(names[3] + "Async", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            if (method == null)
            {
                // 没有找到 Api 的功能方法
                return null;
            }
            
            // 功能的路径
            var funcPath = string.Format("{0}.{1}.{2}.{3}", names[0], names[1], names[2], names[3]);

            // 功能的类型信息
            var types = GetTypes(names[0], names[1], names[2], names[3], funcPath);
            if (types == null)
            {
                return null;
            }

            // 功能的元数据
            var meta = FunctionMeta.Find(types.ArgsType);
            if (meta == null)
            {
                return null;
            }

            // 创建 FunctionModel 的对象实例，如果当前正在上传文件，则不需要创建 FunctionModel 对象
            object funcModel = null;
            if (createModel)
            {
                funcModel = Activator.CreateInstance(types.FuncModelType);
            }

            return new FunctionInfo(funcPath, moduleInfo, types, meta, method, funcModel);
        }
    }
}
